/*
 * helloworld.c
 *
 *  Created on: Mar 31, 2009 6:14:20 PM
 *      Author: lele
 *  All Rights Reseved
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>

#include "rlimit.hpp"
#include "utils.hpp"

extern int errno;

/* "LD_PRELOAD=/libmy.so" only work on dynamic link and only library call */
const char *const env_init[] = { "PATH=.", NULL };

void xcpu_handler(int signo)
{
	exit(SIGXCPU);
}

int wait_status(int status)
{
	if (WIFEXITED(status)) {
		int ret = WEXITSTATUS(status);
		printf("child normal exit,exit code:%d\n", ret);
		return ret;
	}
	if (WIFSIGNALED(status)) {
		int signo = WTERMSIG(status);
		printf("abnormal exit signal number:%d %s\n", signo,
		       str_signal_no(signo));
#ifdef WCOREDUMP
		if (WCOREDUMP(status)) {
			printf("core file generated!\n");
		}
#endif
		return signo;
	}
	if (WIFSTOPPED(status)) {
		int signo = WSTOPSIG(status);
		printf("child stopped:%d %s\n", signo, str_signal_no(signo));
	}
	return 0;
}

int main(int argc, char *argv[])
{
	int status;
	struct rusage usage;
	pid_t pid;

	if (argc < 7) {
		fprintf(stderr, "executor type_id [executable|bytecode] "
			"<input_file> <output_file> "
			"<time_limit> <memory_limit>\n");
		return -1;
	}

	int type_id = atoi(argv[1]);
	char *exe = argv[2];
	char *in_filename = argv[3];
	char *out_filename = argv[4];
	long time_limit = atol(argv[5]);
	long memory_limit = atol(argv[6]);

	if ((pid = fork()) == -1) {
		perror("fork error:%s\n");
		exit(0);
	} else if (pid == 0) {
		int fd[2];

		//printf("now is in the child process:pid is %d.\n", getpid());
		if (chdir("/tmp") != 0) {
			perror("failed to chdir to /tmp");
		}

		if (chroot("/tmp") != 0) {
			perror("failed to chroot");
			exit(1);
		}
		//TODO 1, uid 2, setreuid
		setuid(1000);

		fd[0] = open(in_filename, 'r');
		fd[1] =
		    open(out_filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);

		if (fd[0] == -1) {
			perror("can not open input");
			exit(1);
		}
		if (fd[1] == -1) {
			close(fd[0]);
			perror("can not open output");
			exit(1);
		}
		if (dup2(fd[0], STDIN_FILENO) < 0) {
			perror("can not dup2 to stdin");
			exit(2);
		}

		if (dup2(fd[1], STDOUT_FILENO) < 0) {
			perror("can not dup2 to stdout");
			close(fd[0]);
			exit(2);
		}
		//close unused fds
		close(fd[0]);
		close(fd[1]);

		set_rlimit(time_limit, memory_limit);

		switch (type_id) {
		case 1:
			/* fall through */
		case 2:
			if (access(exe, F_OK) == 0) {
				// file exists
				execle(exe, exe, (char *)NULL, env_init);
			} else {
				// file doesn't exist
				fprintf(stderr, "file do not exist\n");
			}
			break;
		case 3:
			execle("/usr/bin/java", "java", exe, (char *)NULL,
			       env_init);
			break;
		default:
			fprintf(stderr, "unknown type_id:%s\n", argv[1]);
			break;
		}
		fprintf(stderr, "failed to execle:%s\n", strerror(errno));
		return 3;
	}
	//printf("now is in the parent process:pid is %d\n", getpid());
	//setrlimit's RLIMIT_CPU only for CPU time, here for the wall clock             
	signal(SIGALRM, xcpu_handler);
	alarm(time_limit);
	wait4(pid, &status, 0, &usage);
	//print_rlimit();
	//print_rusage(&usage); 
	return wait_status(status);

}
